package com.tianji.remark.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.*;
import com.tianji.remark.constans.RedisConstants;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.dto.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author yang
 * @since 2024-06-28
 */
@Service
@RequiredArgsConstructor
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    /**
     * 点赞和取消
     *
     * @param form
     */
    @Override
    public void likeRecord(LikeRecordFormDTO form) {
        //1.转换类型
        LikedRecord likedRecord = BeanUtils.toBean(form, LikedRecord.class);
        //2.获取用户id
        Long user = UserContext.getUser();
        //3.设置用户id
        likedRecord.setUserId(user);

        //4.执行删除或者是添加
        boolean success = form.getLiked() ? like(likedRecord) : unlike(likedRecord);

        //5.判断是否操作成功
        if (!success) {
            return;
        }

        //6.获取现在的点赞总数
        Long size = redisTemplate.opsForSet().size(RedisConstants.LIKES_BIZ_KEY_PREFIX + likedRecord.getBizId());

        //7.判断数据是否为空
        if (size == null) {
            return;
        }

        //8.更新点赞数
        String key = RedisConstants.LIKES_TIMES_KEY_PREFIX + likedRecord.getBizType();
        redisTemplate.opsForSet().add(key, likedRecord.getBizId().toString(), size.toString());


    }

    /**
     * 查询点赞状态
     *
     * @param bizIds
     * @return
     */
    @Override
    public Set<Long> likeRecordList(List<Long> bizIds) {

        //1.获取用户id
        Long user = UserContext.getUser();

        //2.自定义管道用于一次性执行多次的redis查询，提高查询效率
        List<Object> objects = redisTemplate.executePipelined((RedisCallback<List<Long>>) connection -> {
            StringRedisConnection src = (StringRedisConnection) connection;
            for (Long bizId : bizIds) {
                String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + bizId;
                src.sIsMember(key, user.toString());
            }
            return null;
        });

        //3.处理数据
        Set<Long> collect = IntStream.range(0, bizIds.size())
                .filter(i -> (Boolean) objects.get(i))
                .mapToObj(i -> bizIds.get(i))
                .collect(Collectors.toSet());

        return collect;
    }

    /**
     * 将点赞数同步到redis
     *
     * @param bizType
     * @param maxBizSize
     */
    @Override
    public void readLikedTimesAndSendMessage(String bizType, int maxBizSize) {
        String key = RedisConstants.LIKES_TIMES_KEY_PREFIX + bizType;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);

        while (ObjectUtils.isEmpty(typedTuples)){

            if (CollUtils.isEmpty(typedTuples)) {
                return;
            }
            List<LikedTimesDTO> list = new ArrayList<>(typedTuples.size());
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                Double score = typedTuple.getScore();
                String value = typedTuple.getValue();
                if (score == null || value == null) {
                    continue;
                }

                LikedTimesDTO likedTimesDTO = new LikedTimesDTO();
                likedTimesDTO.setBizId(Long.valueOf(value));
                likedTimesDTO.setLikedTimes(score.intValue());
                list.add(likedTimesDTO);
            }
            // 3.发送MQ消息
            mqHelper.send(
                    LIKE_RECORD_EXCHANGE,
                    StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType),
                    list);

            typedTuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);
        }
    }

    private Boolean unlike(LikedRecord likedRecord) {
        //1.获取Key
        String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + likedRecord.getBizId();
        //2.删除redis
        Long result = redisTemplate.opsForSet().remove(key, likedRecord.getUserId().toString());
        return result != null && result > 0;
    }

    private Boolean like(LikedRecord likedRecord) {
        //1.获取Key
        String key = RedisConstants.LIKES_BIZ_KEY_PREFIX + likedRecord.getBizId();
        //2.添加到redis
        Long result = redisTemplate.opsForSet().add(key, likedRecord.getUserId().toString());
        return result != null && result > 0;
    }
}
